﻿using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Ioc;
using HandyControl.Controls;
using JQ.ServiceManager.Base;
using JQ.ServiceManager.Base.Interfaces;
using JQ.ServiceManager.Base.Models;
using JQ.ServiceManager.views;
using Microsoft.Win32;
using Ninject;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;



namespace JQ.ServiceManager.viewModels
{
    public class MainWindowVM:ViewModelBase
    {
        

        public MainWindowVM()
        {
            container = new SimpleIoc();
            container.Register<LogApi>();
            ServiceItemList = new ObservableCollection<ServiceBase>();
            //load data from config
            coldDownHistory = new Dictionary<string, ColdDownTool>();
            RestartProj = new Dictionary<string, Timer>();
            Reload();
            AutoRefresh();
        }

       

        #region fields and properties 

        private Timer timerOfRefresh;

        private SimpleIoc container;

        //监听服务状态的变化集合
        private Dictionary<string, string> ReverseStatusDics = new Dictionary<string, string>();

        private ServiceBase selectedServiceItem;

        public ServiceBase SelectedServiceItem
        {
            get { return selectedServiceItem; }
            set
            {
                selectedServiceItem = value;
                RaisePropertyChanged(nameof(SelectedServiceItem));
            }
        }

        private ObservableCollection<ServiceBase> serviceItemList;

        public ObservableCollection<ServiceBase> ServiceItemList
        {
            get { return serviceItemList; }
            set { serviceItemList = value; }
        }

        private Dictionary<string, ColdDownTool> coldDownHistory;

        private Dictionary<string,Timer> restartProj;

        public Dictionary<string,Timer> RestartProj
        {
            get { return restartProj; }
            set { restartProj = value; }
        }


        #endregion


        #region Commands
        private RelayCommand browseDirectoryCmd;

        public RelayCommand BrowseDirectoryCmd
        {
            get
            {
                if (browseDirectoryCmd == null)
                {
                    browseDirectoryCmd = new RelayCommand(() =>
                    {
                        if (SelectedServiceItem != null)
                        {
                            string exePath = SelectedServiceItem.ServiceFilePath;
                            string dire = Path.GetDirectoryName(exePath);
                            if (Directory.Exists(dire))
                            {
                                System.Diagnostics.Process.Start("explorer.exe", dire);
                            }
                            else
                            {
                                UIManager.NewInfo($"文件夹:{dire}不存在。");
                            }
                        }
                        else
                        {
                            UIManager.NewInfo("没有选择任何一个服务,无法打开指定的文件夹。");
                        }
                    });
                }
                return browseDirectoryCmd;
            }
        }

        private RelayCommand addServiceCmd;

        public RelayCommand AddServiceCmd
        {
            get
            {
                return addServiceCmd ?? (addServiceCmd = new RelayCommand(() =>
                {
                    NewServiceDialog nsd = new NewServiceDialog();
                    System.Windows.Window winref = new System.Windows.Window();

                    nsd.OnServiceFileConfirm += (pa, pb, pc) =>
                    {
                        winref.Close();
                        string msg;
                        Tuple<string, string, string> serviceDetail = ServiceOperation.RetriveDetil(pb, out msg);
                        if (string.IsNullOrEmpty(msg))
                        {
                            JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Insert(new ServiceBase
                            {
                                ID = null,
                                CustomName = pa,
                                ServiceFilePath = pb,
                                IsWatch = pc,
                                Status = "已添加",
                                ServiceName = serviceDetail.Item1,
                                CreateTime = DateTime.Now,
                                Description = serviceDetail.Item3
                            });
                            Reload(); 
                        }
                        else
                        {
                            JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<LogApi>().Error(msg,"");
                            UIManager.NewInfo(msg);
                        }
                    };

                    nsd.OnServiceDetailConfirm += (smo) =>
                    {
                        winref.Close();
                        ServiceManagementObject serManageObj = smo as ServiceManagementObject;
                        JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Insert(new ServiceBase
                        {
                            ID = null,
                            CustomName = serManageObj.DisplayName,
                            ServiceFilePath = serManageObj.PathName,
                            IsWatch = "是",
                            Status = "已添加",
                            ServiceName = serManageObj.Name,
                            CreateTime = DateTime.Now,
                            Description = ""
                        });
                        Reload();
                    };
                    UIManager.NewDialog("添加服务", nsd, out winref, 720, 400);
                }));
            }
        }

        

        private RelayCommand installServiceCmd;

        public RelayCommand InstallServiceCmd
        {
            get
            {
                return installServiceCmd ?? (installServiceCmd = new RelayCommand(() =>
                {
                    if(SelectedServiceItem != null && !string.IsNullOrEmpty(SelectedServiceItem.ServiceFilePath))
                    {
                        if(ServiceOperation.isServiceIsExisted(SelectedServiceItem.ServiceName))
                        {
                            UIManager.NewInfo("服务已安装，无法重复安装");
                            return;
                        }
                        ServiceOperation.InstallService(null, SelectedServiceItem.ServiceFilePath);
                        SelectedServiceItem.Status = "已安装";
                        JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Update(SelectedServiceItem);
                        Reload();
                    }
                }));
            }
        }

        private RelayCommand launchServiceCmd;

        public RelayCommand LaunchServiceCmd
        {
            get 
            { 
                return launchServiceCmd ?? (launchServiceCmd = new RelayCommand(()=>
                {
                    if(SelectedServiceItem != null)
                    {
                        ServiceOperation.RunService(SelectedServiceItem.ServiceName);
                        //Refresh();
                    }
                })); 
            }
        }

        private RelayCommand stopserviceCmd;

        public RelayCommand StopServiceCmd
        {
            get
            {
                return stopserviceCmd ?? (stopserviceCmd =  new RelayCommand(() =>
                {
                    if (SelectedServiceItem != null)
                    {
                        ServiceOperation.StopService(SelectedServiceItem.ServiceName);
                        //Refresh();
                        JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<LogApi>().Info($"服务{SelectedServiceItem.ServiceName}手动停止。", "");
                    }
                }));
            }
        }

        private RelayCommand uninstallServiceCmd;

        public RelayCommand UninstallServiceCmd
        {
            get
            {
                return uninstallServiceCmd ?? (uninstallServiceCmd = new RelayCommand(() =>
                {
                    if (SelectedServiceItem != null)
                    {
                        if (UIManager.NewAsk($"是否卸载此程序:{SelectedServiceItem.ServiceName}({SelectedServiceItem.CustomName})") == MessageBoxResult.OK)
                        {
                            ServiceOperation.UnInstallService(SelectedServiceItem.ServiceFilePath);
                            SelectedServiceItem.Status = "已卸载";
                            JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Update(SelectedServiceItem);
                            Reload(); 
                        }
                    }
                }));
            }
        }


        private RelayCommand editConfigCmd;

        public RelayCommand EditConfigCmd
        {
            get
            {
                return editConfigCmd ?? (editConfigCmd  = new RelayCommand(() =>
                {
                    if (SelectedServiceItem != null)
                    {
                        System.Windows.Window winref = new System.Windows.Window();
                        string exePath = SelectedServiceItem.ServiceFilePath;
                        if (File.Exists(exePath) && File.Exists($"{exePath}.config"))
                        {
                            var cfgDics = ConfigEditor.GetAppSettings(exePath);
                            UCEditConfig ec = new UCEditConfig(cfgDics);
                            ec.OnModifySetting += (settings) =>
                            {
                                winref.Close();
                                ConfigEditor.UpdateSettings(exePath, settings);
                            };
                            UIManager.NewDialog("修改配置文件", ec, out winref, 700, 400);
                        }
                        else
                        {
                            UIManager.NewInfo("文件不存在！");
                        }
                    }
                }));
            }
        }

        private RelayCommand<ServiceBase> deleteServiceCmd;

        public RelayCommand<ServiceBase> DeleteServiceItemCmd
        {
            get
            {
                return deleteServiceCmd ?? (deleteServiceCmd =  new RelayCommand<ServiceBase>((p) =>
                {
                if (SelectedServiceItem != null)
                    {
                        if (UIManager.NewAsk($"确认删除该条目:{SelectedServiceItem.ServiceName}({SelectedServiceItem.CustomName}) ?") == MessageBoxResult.OK)
                        {
                            JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Delete(SelectedServiceItem);
                            Reload(); 
                        }
                    }
                }));
            }
        }

        private RelayCommand setupCmd;

        public RelayCommand SetupCmd
        {
            get
            {
                return setupCmd ?? (setupCmd = new RelayCommand(() =>
                {
                    System.Windows.Window winref = new System.Windows.Window();
                    UCSetting sett = new UCSetting();
                    UIManager.NewDialog("设置",sett, out winref, 700, 550);
                }));
            }
        }

        private RelayCommand logViewCmd;

        public RelayCommand LogViewCmd
        {
            get
            {
                return logViewCmd ?? (logViewCmd = new RelayCommand(() =>
                {

                    System.Windows.Window winref = new System.Windows.Window();
                    UCLogView logView = new UCLogView();
                    UIManager.NewDialog("日志查询", logView, out winref, 700, 450);
                }));
            }
        }


        private RelayCommand editCmd;

        public RelayCommand EditCmd
        {
            get
            {
                return editCmd ?? (editCmd = new RelayCommand(() =>
                {
                    if (SelectedServiceItem != null)
                    {
                        System.Windows.Window winref = new System.Windows.Window();
                        UCEditServiceBase ucEditServ = new UCEditServiceBase(SelectedServiceItem);
                        ucEditServ.OnModifyServicePropertyCompleted += () =>
                        {
                            winref.Close();
                            Reload();
                        };
                        UIManager.NewDialog("服务属性编辑", ucEditServ, out winref, 500, 500);
                    }
                }));
            }
        }

        private RelayCommand browseLogCmd;

        public RelayCommand BrowseLogCmd
        {
            get
            {
                return browseLogCmd ?? new RelayCommand(() =>
                {
                    if (SelectedServiceItem != null)
                    {
                        if (!string.IsNullOrEmpty(SelectedServiceItem.LogRelativePath))
                        {
                            int year = DateTime.Now.Year;
                            int month = DateTime.Now.Month;
                            int day = DateTime.Now.Day;
                            FileInfo fi = new FileInfo(SelectedServiceItem.ServiceFilePath);
                            string parentDir = fi.Directory.FullName;
                            string relativePath = SelectedServiceItem.LogRelativePath.Replace("@year", year.ToString()).Replace("@month", month.ToString()).Replace("@day", day.ToString());
                            string absolutePath = Path.Combine(parentDir, relativePath);
                            if (File.Exists(absolutePath))
                            {
                                Process.Start(absolutePath); 
                            }
                            else
                            {
                                UIManager.NewInfo($"日志文件{absolutePath}不存在！");
                            }
                        }
                        else
                        {
                            UIManager.NewInfo("日志路径未配置！");
                        }
                    }
                });
            }
        }

        #endregion

        #region methods

        private void AutoRefresh()
        {
            string refreshInterval = SysParamApi.SysParams["RefreshInterval"];
            int interval = 0;
            int.TryParse(refreshInterval, out interval);
            if (interval > 0)
            {
                timerOfRefresh = new Timer();
                timerOfRefresh.Interval = interval;
                timerOfRefresh.AutoReset = true;
                timerOfRefresh.Elapsed += (s, e) =>
                {
                    Refresh();
                };
                timerOfRefresh.Start();
            }
        }

        private void Reload()
        {
            ServiceItemList.Clear();
            var serItems = JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<IRepo<ServiceBase>>().Get(i => 1 == 1);
            if(serItems != null && serItems.Any())
            {
                serItems.ForEach(it => { ServiceItemList.Add(it); });
            }

            if (serItems != null && serItems.Count > 0)
            {
                //自动重启业务
                foreach (var serItem in serItems)
                {
                    if (serItem.TriggerHour > 0 && serItem.TriggerMinute > 0)
                    {
                        //add restart jobs
                        Timer tim = new Timer(50 * 1000);
                        tim.Elapsed += (s, e) =>
                        {
                            int HOUR = e.SignalTime.Hour;
                            int Minute = e.SignalTime.Minute;
                            int Second = e.SignalTime.Second;
                            if (HOUR == serItem.TriggerHour && Minute == serItem.TriggerMinute && Second > 10)
                            {
                                container.GetInstance<LogApi>().Info($"准备重启服务'{serItem.CustomName}({serItem.ServiceName})'", "");
                                if (!string.IsNullOrEmpty(serItem.ProcessName))
                                {
                                    string err = ServiceOperation.KillProcess(serItem.ProcessName);
                                    if (!string.IsNullOrEmpty(err))
                                    {
                                        container.GetInstance<LogApi>().Error($"终止进程失败:{err}", "");
                                    }
                                    else
                                    {
                                    //尝试重启:
                                    Task.Delay(5000).Wait();
                                        try
                                        {
                                            ServiceOperation.RunService(serItem.ServiceName);
                                        }
                                        catch (Exception ex)
                                        {
                                            container.GetInstance<LogApi>().Error($"重启服务失败:{ex.Message}", ex.StackTrace);
                                        }
                                    }
                                }
                                else
                                {
                                    container.GetInstance<LogApi>().Error("服务{serItem.CustomName}缺失进程名，重启失败。", "");
                                }
                            }
                        };

                        tim.Start();
                        if (!RestartProj.ContainsKey(serItem.ServiceName))
                        {
                            //新增
                            RestartProj.Add(serItem.ServiceName, tim);
                        }
                        else
                        {
                            //替换
                            Timer timCurrent = RestartProj[serItem.ServiceName];
                            timCurrent.Stop();
                            timCurrent.Dispose();
                            RestartProj.Remove(serItem.ServiceName);
                            RestartProj.Add(serItem.ServiceName, tim);
                        }
                    }
                } 
            }
        }

        private void Refresh()
        {
            var servStatusList = ServiceItemList.Where(it =>  it.Status != "已卸载").Select(it => new ServiceBase() { ServiceName = it.ServiceName, Status = it.Status }).ToList();
            ServiceOperation.GetServiceStatus(ref servStatusList);
            servStatusList.ForEach((serv) =>
            {
                var currServ = ServiceItemList.Where(it => it.ServiceName == serv.ServiceName).FirstOrDefault();
                currServ.Status = serv.Status;


                if (currServ.Status == "停止")
                {
                    string recordStatus = string.Empty;
                    ReverseStatusDics.TryGetValue(currServ.ServiceName, out recordStatus);
                    if (ReverseStatusDics != null && recordStatus == "运行")
                    {
                        //当且仅当从“运行”状态转为“停止”状态，记录日志
                        container.GetInstance<LogApi>().Error($"服务{currServ.ServiceName}停止。", "");
                    }
                    else
                    {
                        if (ReverseStatusDics.ContainsKey(currServ.ServiceName))
                        {
                            ReverseStatusDics[currServ.ServiceName] = currServ.Status;
                        }
                        else
                        {
                            ReverseStatusDics.Add(currServ.ServiceName, currServ.Status);
                        }
                    }

                    //如果守护则启动此服务
                    if(currServ.IsWatch == "是")
                    {
                        ServiceOperation.RunService(currServ.ServiceName);
                        string coldDownDtr = SysParamApi.SysParams["AutoRestartServiceColdDown"];
                        int _coldDown = 0;
                        int.TryParse(coldDownDtr, out _coldDown);

                        if (_coldDown == 0)
                        {
                            _coldDown = 15;
                        }

                        //是否在重启冷却中
                        if (coldDownHistory.ContainsKey(currServ.ServiceName))
                        {
                            //冷却完毕，仍未启动，再次重启，直到重启成功
                            if(coldDownHistory[currServ.ServiceName].IsDone)
                            {
                                coldDownHistory[currServ.ServiceName].TimerOfColdDown.Stop();
                                coldDownHistory[currServ.ServiceName].TimerOfColdDown.Dispose();

                                coldDownHistory[currServ.ServiceName] = new ColdDownTool(_coldDown);
                                ServiceOperation.RunService(currServ.ServiceName);
                                JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<LogApi>().Info($"服务:{currServ.ServiceName} 重启后失败，正在重新启动...","");
                            }
                        }
                        else
                        {
                            //不在冷却中，属于第一次重启：
                            coldDownHistory.Add(currServ.ServiceName, new ColdDownTool(_coldDown));
                            ServiceOperation.RunService(currServ.ServiceName);
                            JQ.ServiceManager.misc.NinjectContainer.NinjectCore.Get<LogApi>().Info($"服务{currServ.ServiceName} 已停止，正在尝试重启...", "");
                        }
                    }
                }
                else if(currServ.Status == "运行")
                {
                    if(coldDownHistory.ContainsKey(currServ.ServiceName))
                    {
                        (coldDownHistory[currServ.ServiceName] as ColdDownTool).TimerOfColdDown.Dispose();
                        coldDownHistory.Remove(currServ.ServiceName);
                    }
                }
            });
        }

        public void Clean()
        {
            timerOfRefresh.Stop();
            timerOfRefresh.Dispose();
        }
        #endregion
    }
}
